#include "../include/mytools.h"

namespace zzyserver
{
    bool MyTools::parseopt(int argc, char *argv[], Popt opt, struct option *long_options, std::string shortopts, bool fserver)
    {
        int optc;
        while (1)
        {
            int this_option_optind = optind ? optind : 1;
            int option_index = 0;
            

            optc = getopt_long(argc, argv, shortopts.c_str(),
                            long_options, &option_index);
            if (optc == -1)
                break;
            if(fserver)
            {
                switch (optc)
                {
                case 0:
                    printf("option %s", long_options[option_index].name);
                    if (optarg)
                        printf(" with arg %s", optarg);
                    printf("\n");
                    return false;
                    break;
                case 'l':
                    opt->ip = optarg;
                    break;

                case 'H':
                    opt->hidden = true;
                    break;

                case 'p':
                    opt->port = atoi(optarg);
                    break;

                case 'h':
                    printf("Usage: fserver [OPTION] ... <DIR>\n\
    serve some files over tcp\n\
    \n\
    args:\n\
        <DIR> Which directory to serve\n\
    \n\
    options:\n\
        -l, --listen=ADDR       specify source address to use [default is localhost]\n\
        -H, --hidden            show hidden file\n\
        -p, --port=PORT         specify listening port [default port is 12345]\n\
        -h, --help              display this help and exit\n");
                    return false;
                    break;

                case '?':
                default:
                    printf("?? getopt returned character code 0%o ??\n", optc);
                    return false;
                }
            }
            else
            {
                switch (optc)
                {
                case 0:
                    printf("option %s", long_options[option_index].name);
                    if (optarg)
                        printf(" with arg %s", optarg);
                    printf("\n");
                    return false;
                    break;
                case 'l':
                    opt->show = true;
                    break;

                case 's':
                    opt->ip = optarg;
                    break;

                case 'p':
                    opt->port = atoi(optarg);
                    break;
                
                case 'd':
                    opt->sourcename = optarg;
                    break;

                case 'o':
                    opt->outfilename = optarg;
                    break;

                case 'h':
                    printf("Usage: fclient [OPTION] ...\
    download file from fserver over tcp \n\
    options:\n\
        -l, --list                 show remote served files [default action]\n\
        -s, --server            specify server address [default addr is localhost]\n\
        -p, --port                 specify server port [default port is 12345]\n\
        -d, --dl=FILE              download specified file\n\
        -o, --output=FILE        output to the FILE\n\
        -h, --help                display this help and exit\n");
                    return false;
                    break;

                case '?':
                default:
                    printf("?? getopt returned character code 0%o ??\n", optc);
                    return false;
                }
            }
        }

        if (optind < argc-1)
        {
            printf("non-option ARGV-elements: ");
            while (optind < argc)
                printf("%s ", argv[optind++]);
            printf("\n");
        }
        if(opt->port > MAX_PORT_NUM)
        {
            printf("this port: %d is invaild.\n", opt->port);
            return false;
        }
        if(!is_ipv4_addr(opt->ip))
        {
            printf("failed to start fserver: %s is not a valid address\n", opt->ip.c_str());
            return false;
        }
        return true;
    }

    int MyTools::judgefile(std::string filepath)
    {
        struct stat s;
        if (stat(filepath.c_str(), &s) == 0)
        {
            if (s.st_mode & __S_IFDIR)
            {
                return 2;
            }
            else if (s.st_mode & __S_IFREG)
            {
                return 1;
            }
            else
                return 0;
        }
        return -1;
    }

    long MyTools::get_file_size(std::string file_name)
    {
        if(access(file_name.c_str(), F_OK))
        {
            printf("file: %s can not open!\n", file_name.c_str());
            return false;
        }

        FILE *fp = fopen(file_name.c_str(), "rb");
        if(NULL == fp )
        {
            printf("file: %s can not open!\n", file_name.c_str());
            return false;
        }

        long file_size = 0;
        fseek(fp, 0, SEEK_END); //把文件内部指针移动到文件尾部
        file_size = ftell(fp); //返回指针偏离文件头的位置(即文件中字符个数)
        fseek(fp, 0, SEEK_SET); //把文件内部指针移回到文件头部

        fclose(fp);

        return file_size;
    }

    int MyTools::is_exist_file(std::string filepath, std::string filename)
    {
        DIR *p_dir = NULL;
    
        //定义一个目录结构体指针
        struct dirent *p_dirent = NULL;
    
        //打开目录，返回一个目录流指针指向第一个目录项
        p_dir = opendir(filepath.c_str());
        if(p_dir == NULL)
        {
            printf("opendir error\n");
        }
    
        //循环读取每个目录, 当返回NULL时读取完
        while((p_dirent = readdir(p_dir)) != NULL)
        {
            if(!strcmp(p_dirent->d_name, ".") || !strcmp(p_dirent->d_name, ".."))
                continue;
            if(filename == p_dirent->d_name)
            {
                std::string filepath_name = filepath;
                if(filepath_name[filepath_name.size()-1] == '\0')filepath_name[filepath_name.size()-1] = '/';
                else filepath_name += "/";
                filepath_name += filename;
                return judgefile(filepath_name);
            }
        }
        return -1;

    }

    int MyTools::TraverseDir(std::string direntName, int level, bool flag)
    {
        //定义一个目录流指针
        DIR *p_dir = NULL;
    
        //定义一个目录结构体指针
        struct dirent *p_dirent = NULL;
        int totolsize = 0;
        //打开目录，返回一个目录流指针指向第一个目录项
        p_dir = opendir(direntName.c_str());
        if(p_dir == NULL)
        {
            return printf("opendir: %s error.\n", direntName.c_str());
        }
    
        //循环读取每个目录, 当返回NULL时读取完
        while((p_dirent = readdir(p_dir)) != NULL)
        {   
            if(!strcmp(p_dirent->d_name, ".") || !strcmp(p_dirent->d_name, ".."))
                continue;
            if(!flag)
            {
                if(p_dirent->d_name[0] == '.')
                    continue;
            }
            int i;
            for(i = 0; i < level; i++)
            {
                totolsize += printf("    |");
            }
            totolsize += printf("    %s\n", p_dirent->d_name);
    
            //如果目录项仍是一个目录的话，进入目录
            if(p_dirent->d_type == DT_DIR)
            {
                //准备下一个
                std::string backupDirName = direntName;
                if(backupDirName[backupDirName.size()-1] =='\0')
                    backupDirName[backupDirName.size()-1] =  '/';
                else backupDirName += '/';
                backupDirName += p_dirent->d_name;
                totolsize += TraverseDir(backupDirName, level + 1, flag);//递归调用
            }
        }
    
        closedir(p_dir);
        return totolsize;
    }

    bool MyTools::is_ipv4_addr(std::string ip)
    {
        if(ip == "localhost" || ip == "0.0.0.0")return true;
        
        if (!ip.size() || ip[0] == '0' || ip[0] == '\0') {
            return false;
        }

        for (int i = 0, count = 0; i < ip.size(); i++) {
            if ((ip[i] != '.') && (ip[i] < '0' || ip[i] > '9')) {
                return false;
            }
            if (ip[i] == '.') {
                count++;
                if (count > 3) {
                    return false;
                }
            }
        }

        int ip_num[4] = {-1, -1, -1, -1};
        char ip_s[4][4];
        memset(ip_s, 0, sizeof(char[4]) * 4);

        sscanf(ip.c_str(), "%[^.].%[^.].%[^.].%[^ ]", ip_s[0], ip_s[1], ip_s[2], ip_s[3]);
        sscanf(ip.c_str(), "%d", &ip_num[0]);
        sscanf(ip_s[1], "%d", &ip_num[1]);
        sscanf(ip_s[2], "%d", &ip_num[2]);
        sscanf(ip_s[3], "%d", &ip_num[3]);

        for (int i = 0; i < 4; i++) {
            if (strlen(ip_s[i]) == 0 || (ip_s[i][0] == '0' && ip_s[i][1] != '\0') || ip_num[i] < 0 || ip_num[i] > 255) {
                return false;
            }
        }

        return true;
    }

    bool MyTools::judgepath(const std::string& path)
    {
        // 遍历路径判断是否超出范围，即..是否过多，如果超出范围则返回false
        std::vector<std::string> path_vec;
        std::string temp = path;
        while (temp.size() > 0) {
            int pos = temp.find('/');
            if (pos == std::string::npos) {
                path_vec.push_back(temp);
                break;
            }
            path_vec.push_back(temp.substr(0, pos));
            temp = temp.substr(pos + 1);
        }

        int count = 0;
        for (int i = 0; i < path_vec.size(); i++) {
            if (path_vec[i] == "..") 
                count++;
            else if (path_vec[i] != ".")
                count--;
            if (count > 0) 
                return false;
        }
        return true;
    }
}